package com.wonders.frame.chron.controller;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import com.wonders.frame.chron.config.ChronConfig;
import com.wonders.frame.chron.model.bo.Summary;
import com.wonders.frame.chron.service.WebUtilService;
import com.wonders.frame.chron.util.LoginConstants;
import com.wonders.frame.chron.util.StringUtil;
import com.wonders.frame.console.controller.AbstractGenericRelationTreeObjController;
import com.wonders.frame.core.model.bo.Relation;
import com.wonders.frame.core.model.vo.FieldProperty;
import com.wonders.frame.core.model.vo.PageInfo;
import com.wonders.frame.core.model.vo.ReturnObj;
import com.wonders.frame.core.model.vo.SimplePage;
import com.wonders.frame.core.model.vo.SingleModelParams;
import com.wonders.frame.core.model.vo.SingleQueryParams;
import com.wonders.frame.core.service.RelationService;
import com.wonders.frame.core.utils.ReflectUtil;
import com.wonders.frame.core.utils.SingleParamsConvertUtil;

public abstract class BasicChronController<T> extends AbstractGenericRelationTreeObjController<T>{
	private final Logger logger = LoggerFactory.getLogger(this.getClass());
	private static int RULE_TYPE_ID = 1;
	
	@Resource
	private RelationService relationService;
	@Resource
	ChronConfig chronConfig;
	@Autowired
	WebUtilService webUtilService;
	
	@ModelAttribute
	public T get(@RequestParam(required=false) String id) {
		T entity = null;
		try {
			if (StringUtils.isNotBlank(id)){
				entity = (T)this.getBasicCrudService().get(this.getEntityClass(), Integer.valueOf(id)).getData();
			}else{
				entity = (T)this.getEntityClass().newInstance();				
			}
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} 
		return entity;
	}
	
	@RequestMapping(value={"/list"})
	public String list(T model, HttpServletRequest request)
	{
		getPageData(request);
		return this.getEntityName() + "/" + this.getEntityName() + "List";
	}
	
	@RequestMapping(value={"/list/{path}"})
	public String list(T model, @PathVariable("path") String path, HttpServletRequest request)
	{
		getPageData(request);
		return path + "/" + this.getEntityName() + "List";
	}
	
	@RequestMapping(value={"/add"})
	public String add(HttpServletRequest request)
	{
		getFieldData(request);
		return this.getEntityName() + "/" + this.getEntityName() + "Add";
	}
	
	@RequestMapping(value={"/add/{path}"})
	public String add(@PathVariable("path") String path, HttpServletRequest request)
	{
		getFieldData(request);
		return path + "/" + this.getEntityName() + "Add";
	}
	
	@RequestMapping(value={"/view"})
	public String view(HttpServletRequest request)
	{
		getFieldData(request);
		getJeesiteAddress(request);
		return this.getEntityName() + "/" + this.getEntityName() + "View";
	}
	
	@RequestMapping(value={"/view/{path}"})
	public String view(@PathVariable("path") String path, HttpServletRequest request)
	{
		getJeesiteAddress(request);
		return path + "/" + this.getEntityName() + "View";
	}
	
	@RequestMapping(value={"/edit"})
	public String edit(HttpServletRequest request)
	{
		getFieldData(request);
		getJeesiteAddress(request);
		return this.getEntityName() + "/" + this.getEntityName() + "Edit";
	}
	
	
	@RequestMapping(value={"/compliedit"})
	public String compliedit(HttpServletRequest request)
	{
		getFieldData(request);
		getJeesiteAddress(request);
		return this.getEntityName() + "/" + this.getEntityName() + "ComplieEdit";
	}
	
	
	@RequestMapping(value={"/edit/{path}"})
	public String edit(@PathVariable("path") String path, HttpServletRequest request)
	{
		getFieldData(request);
		getJeesiteAddress(request);
		return path + "/" + this.getEntityName() + "Edit";
	}
	
	@RequestMapping(value={"/save"}, method={org.springframework.web.bind.annotation.RequestMethod.POST})
	@ResponseBody
	public ReturnObj save(HttpServletRequest request)
	{
		try
	    {
			SingleModelParams smp = SingleParamsConvertUtil.getModelParams(this.getEntityClass(), request);
			addSessionInfo(smp, request.getSession());
			addExtraOnSave(request, smp);
			return this.getBasicCrudService().saveOrUpdate(smp);
	    }
	    catch (Exception e)
	    {
	      return new ReturnObj(e);
	    }		
	}
	
	public void getPageData(HttpServletRequest request){
		
		SingleQueryParams sqp = SingleParamsConvertUtil.getQueryParams(this.getEntityClass(), request);
		
		sqp.addSort("id", "desc");
		
		String pId = request.getParameter("pId");
		String pType = request.getParameter("pType");
		String lv = request.getParameter("lv");
		if(StringUtils.isNotEmpty(pId) && StringUtils.isNotEmpty(pType)){
			sqp.setConditionExtra(filterRelatedChilds(pId, pType, lv));
		}
		
		Integer pageNo = (StringUtils.isNotEmpty(request.getParameter("pageNo")) ? Integer.valueOf(request.getParameter("pageNo")) : 1);
		Integer pageSize = (StringUtils.isNotEmpty(request.getParameter("pageSize")) ? Integer.valueOf(request.getParameter("pageSize")) : 5);
		sqp.setRange1(pageNo);
		sqp.setRange2(pageSize);
		
		SimplePage page = this.getBasicCrudService().findByPage(sqp).getData();
		
		request.setAttribute("list", page.getContent());
		request.setAttribute("page", page.getPageInfo());
		request.setAttribute("pagination", this.genPaging(page.getPageInfo()));
		Map<String , String>  flowUser = new HashMap();
		for(Object obj : page.getContent()){
			Summary summary = (Summary)  obj;
			//summary.getProcessInstanceId()
			List<Map<String, String>> result = new ArrayList<Map<String,String>>();
			try {
				result = webUtilService.getComments(summary.getProcessInstanceId());
				for (Map<String, String> map : result) {
					String userId = map.get("userId");
					if(flowUser.containsKey(summary.getProcessInstanceId())){
						
						String users = flowUser.get(summary.getProcessInstanceId());
						flowUser.put(summary.getProcessInstanceId(), users+ userId+",");
					}else{
						flowUser.put(summary.getProcessInstanceId(), userId+",");
					}
					
				}
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			
			
		}
		request.setAttribute("flowUsers", flowUser);		
		List<FieldProperty> fieldProperties = this.getBasicCrudService().getProperty(this.getEntityClass(), null, false).getData().getFieldProperties();
		request.setAttribute("fields", this.list2Map(fieldProperties));
		
		request.setAttribute("params", sqp.getData());
	}
	
	public void getFieldData(HttpServletRequest request){
		
		List<FieldProperty> fieldProperties = this.getBasicCrudService().getProperty(this.getEntityClass(), null, false).getData().getFieldProperties();
		request.setAttribute("fields", this.list2Map(fieldProperties));
	}
	
	public void getJeesiteAddress(HttpServletRequest request){
		
		request.setAttribute("jeesiteAddress", chronConfig.getFlowCenter());
	}
	
	/**
	 * 过滤与父元素关联的子元素
	 * @param pId
	 * @param pType
	 * @param lv
	 * @return
	 */
	@SuppressWarnings("unchecked")
	private String filterRelatedChilds(String pId, String pType, String lv){
		String idCondition = "";
		try {
			
			String ntype = this.getEntityName();
			List<String> ntypeList = new ArrayList<String>();
			ntypeList.add(ntype);
			
			List<Integer> idList = new ArrayList<Integer>();
			
			if("-1".equals(lv)){
				idList = (List<Integer>)ReflectUtil.executeMethod("catalog", "findSpecificTypeChildren", 
		                new Class[] { String.class, String.class, List.class }, new Object[] { pId, pType, ntypeList});
			}else{
				//默认取一级
				List<Relation> relations = relationService.findAllChildRelationByNextTypes(RULE_TYPE_ID, pType, Integer.valueOf(pId), ntypeList, 1);
				
			    for (Relation r : relations)
			    {
			        if (!idList.contains(r.getNid())) {
			        	idList.add(r.getNid());
			        }
			    }				
			}
			
		    if(idList.size() > 0){
		    	if(idList.size() > 1000){
		    		List<List<?>> splitList = StringUtil.splitList(idList, 1000);
		    		for(List<?> subList : splitList){
		    			if(idCondition.length() == 0){
		    				idCondition += " o.id in ("+StringUtils.join(subList, ",")+")";		    				
		    			}else{
		    				idCondition += " or o.id in ("+StringUtils.join(subList, ",")+")";
		    			}
		    		}
		    	}else{
		    		idCondition = " o.id in ("+StringUtils.join(idList, ",")+")";		    		
		    	}
		    }else{
		    	idCondition = " 1=2";
		    }
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return idCondition;
	}
	
	private String genPaging(PageInfo page) {
		
		int pageNo = page.getCurrentPage(); // 当前页码
		int pageSize = page.getPageSize(); // 页面大小，设置为“-1”表示不进行分页（分页无效）
		
		long count = page.getTotalRecord();// 总记录数，设置为“-1”表示不查询总数
		
		int first;// 首页索引
		int last;// 尾页索引
		int prev;// 上一页索引
		int next;// 下一页索引
		
		boolean firstPage;//是否是第一页
		boolean lastPage;//是否是最后一页

		int length = 8;// 显示页面长度
		int slider = 1;// 前后显示页面长度
		
		List<T> list = new ArrayList<T>();
		String orderBy = ""; // 标准查询有效， 实例： updatedate desc, name asc
		String funcName = "page"; // 设置点击页码调用的js函数名称，默认为page，在一页有多个分页对象时使用。
		String funcParam = ""; // 函数的附加参数，第三个参数值。
		String message = ""; // 设置提示消息，显示在“共n条”之后
		
		//initialize
		first = 1;
		
		last = (int)(count / (pageSize < 1 ? 20 : pageSize) + first - 1);
		
		if (count % pageSize != 0 || last == 0) {
			last++;
		}

		if (last < first) {
			last = first;
		}
		
		if (pageNo <= 1) {
			pageNo = first;
			firstPage=true;
		}

		if (pageNo >= last) {
			pageNo = last;
			lastPage=true;
		}

		if (pageNo < last - 1) {
			next = pageNo + 1;
		} else {
			next = last;
		}

		if (pageNo > 1) {
			prev = pageNo - 1;
		} else {
			prev = first;
		}
		
		//2
		if (pageNo < first) {// 如果当前页小于首页
			pageNo = first;
		}

		if (pageNo > last) {// 如果当前页大于尾页
			pageNo = last;
		}
		
		
		
		//build String
		StringBuilder sb = new StringBuilder();
		
		if (pageNo == first) {// 如果是首页
			sb.append("<li class=\"disabled\"><a href=\"javascript:\">&#171; 上一页</a></li>\n");
		} else {
			sb.append("<li><a href=\"javascript:\" onclick=\""+funcName+"("+prev+","+pageSize+",'"+funcParam+"');\">&#171; 上一页</a></li>\n");
		}

		int begin = pageNo - (length / 2);

		if (begin < first) {
			begin = first;
		}

		int end = begin + length - 1;

		if (end >= last) {
			end = last;
			begin = end - length + 1;
			if (begin < first) {
				begin = first;
			}
		}

		if (begin > first) {
			int i = 0;
			for (i = first; i < first + slider && i < begin; i++) {
				sb.append("<li><a href=\"javascript:\" onclick=\""+funcName+"("+i+","+pageSize+",'"+funcParam+"');\">"
						+ (i + 1 - first) + "</a></li>\n");
			}
			if (i < begin) {
				sb.append("<li class=\"disabled\"><a href=\"javascript:\">...</a></li>\n");
			}
		}

		for (int i = begin; i <= end; i++) {
			if (i == pageNo) {
				sb.append("<li class=\"active\"><a href=\"javascript:\">" + (i + 1 - first)
						+ "</a></li>\n");
			} else {
				sb.append("<li><a href=\"javascript:\" onclick=\""+funcName+"("+i+","+pageSize+",'"+funcParam+"');\">"
						+ (i + 1 - first) + "</a></li>\n");
			}
		}

		if (last - end > slider) {
			sb.append("<li class=\"disabled\"><a href=\"javascript:\">...</a></li>\n");
			end = last - slider;
		}

		for (int i = end + 1; i <= last; i++) {
			sb.append("<li><a href=\"javascript:\" onclick=\""+funcName+"("+i+","+pageSize+",'"+funcParam+"');\">"
					+ (i + 1 - first) + "</a></li>\n");
		}

		if (pageNo == last) {
			sb.append("<li class=\"disabled\"><a href=\"javascript:\">下一页 &#187;</a></li>\n");
		} else {
			sb.append("<li><a href=\"javascript:\" onclick=\""+funcName+"("+next+","+pageSize+",'"+funcParam+"');\">"
					+ "下一页 &#187;</a></li>\n");
		}

		sb.append("<li class=\"disabled controls\"><a href=\"javascript:\">第 ");
		sb.append("<input type=\"text\" value=\""+pageNo+"\" onkeypress=\"var e=window.event||this;var c=e.keyCode||e.which;if(c==13)");
		sb.append(funcName+"(this.value,"+pageSize+",'"+funcParam+"');\" onclick=\"this.select();\"/>页，每页 ");
		
		sb.append("<select onchange=\""+funcName+"(1,this.value,'"+funcParam+"');\">");
		sb.append("<option value=\"5\" "+(pageSize==5?"selected":"")+">5</option>");
		sb.append("<option value=\"10\" "+(pageSize==10?"selected":"")+">10</option>");
		sb.append("<option value=\"20\" "+(pageSize==20?"selected":"")+">20</option>");
		sb.append("<option value=\"50\" "+(pageSize==50?"selected":"")+">50</option>");
		sb.append("</select> 条，");
		
/*		sb.append("<input type=\"text\" value=\""+pageSize+"\" onkeypress=\"var e=window.event||this;var c=e.keyCode||e.which;if(c==13)");
		sb.append(funcName+"("+pageNo+",this.value,'"+funcParam+"');\" onclick=\"this.select();\"/> 条，");*/
		sb.append("共 " + count + " 条"+(message!=null?message:"")+"</a></li>\n");

		sb.insert(0,"<ul>\n").append("</ul>\n");
		
		sb.append("<div style=\"clear:both;\"></div>");

//		sb.insert(0,"<div class=\"page\">\n").append("</div>\n");
		
		return sb.toString();
	}
	
	private void addSessionInfo(SingleModelParams smp, HttpSession session){
		smp.addData("operateUserId", (String)session.getAttribute(LoginConstants.USERID));
		smp.addData("operateDeptId", (String)session.getAttribute(LoginConstants.DEPTID));
		smp.addData("operateUser", (String)session.getAttribute(LoginConstants.USERNAME));
		smp.addData("operateDept", (String)session.getAttribute(LoginConstants.DEPTNAME));	
		
		smp.addData("uploader", (String)session.getAttribute(LoginConstants.USERNAME));
		smp.addData("unit", (String)session.getAttribute(LoginConstants.DEPTNAME));
	}
	
	public void addExtraOnSave(HttpServletRequest request, SingleModelParams smp){}
	
	private Map<String, FieldProperty> list2Map(List<FieldProperty> list) {  
	  
        Map<String, FieldProperty> map = new HashMap<String, FieldProperty>();  
  
        if ((list != null) && (list.size() != 0)) {  
            for (FieldProperty property : list) {  
                map.put(property.getPath(), property);  
            }  
        }  
        return map;  
    }  
}
